package cn.congine.wizarpos.mall.service.impl;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;

import cn.congine.wizarpos.mall.exception.MangoException;
import cn.congine.wizarpos.mall.model.SysInTicket;
import cn.congine.wizarpos.mall.model.SysWxChildMerchantToken;
import cn.congine.wizarpos.mall.service.SysInTicketService;
import cn.congine.wizarpos.mall.service.SysMerchantDefService;
import cn.congine.wizarpos.mall.service.SysWxChildMerchantTokenService;
import cn.congine.wizarpos.mall.utils.SysConstants;
import cn.congine.wizarpos.mall.utils.WxSignUtil;
import cn.congine.wizarpos.mall.vo.WxAccessToken;

import com.alibaba.fastjson.JSONObject;

@Service("wxAuthentication")
public class WxAuthentication {

	@Resource(name = "messageSource")
	private MessageSource messageSource;

	@Autowired
	private WPosHttpClient wposHttpClient;

	@Autowired
	private SysMerchantDefService merchantDefService;

	@Autowired
	private SysInTicketService sysInTicketService;
	
	@Autowired
	private SysWxChildMerchantTokenService sysWxChildMerchantTokenService;
	
	/**
	 * 获取微信 access token
	 * 
	 * @return
	 * @throws Exception
	 * @throws MangoException
	 */
	public WxAccessToken getAccessToken(String mid) throws Exception {
		System.out.println("----------WxAuthentication.getAccessToken------------");
		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		String accessToken = wxAuthorizationInfo.getMerchantAccessToken();
		Long timestamp = wxAuthorizationInfo.getMerchantAccessTokenExpiry();
		
		WxAccessToken wxAccessToken = null;
		long now = WxSignUtil.createTimestamp();
		if (accessToken == null || (now - timestamp) > 7200L
				|| isInvalid(accessToken)) {
			wxAccessToken = updateAccessToken(mid);
		} else {
			wxAccessToken = new WxAccessToken();
			wxAccessToken.setAccessToken(accessToken);
			wxAccessToken.setTimestamp(timestamp);
			wxAccessToken.setMid(mid);
		}

		return wxAccessToken;
	}

	/**
	 * 获取 微信 js api ticket
	 * 
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	public String getJsTicket(String mid) throws Exception {
		System.out.println("----------WxAuthentication.getJsTicket------------");
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.JSNNAME, mid);

		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		String accessToken = wxAuthorizationInfo.getMerchantAccessToken();
		Long timestamp = wxAuthorizationInfo.getMerchantAccessTokenExpiry();

		String ticket = null;
		long now = WxSignUtil.createTimestamp();
		if (accessToken == null || (now - timestamp) > 7200L
				|| isInvalid(accessToken)) {// 过期以access token 的时间为准,
													// accessToken过期，ticket肯定无效
			WxAccessToken wxAccessToken = updateAccessToken(mid);
			ticket = updateJsTicket(wxAccessToken.getAccessToken(),
					wxAccessToken.getTimestamp(), mid);
		} else if (sysInTicket == null || sysInTicket.getWxTicket() == null
				|| (now - sysInTicket.getExpiryTime()) > 7200L) {
			// accessToken有效，ticket不一定有效，仍要判断时间，因为accesstoken和ticket不是一一对应关系，
			// 此情况下更新ticket
			ticket = updateJsTicket(accessToken, timestamp, mid);
		} else {
			ticket = sysInTicket.getWxTicket();
		}

		return ticket;
	}

	/**
	 * 获取 微信 js api ticket
	 * 
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	public String getCardTicket(String mid) throws Exception {
		System.out.println("----------WxAuthentication.getCardTicket------------");
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.CARDNAME, mid);

		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		String accessToken = wxAuthorizationInfo.getMerchantAccessToken();
		Long timestamp = wxAuthorizationInfo.getMerchantAccessTokenExpiry();

		String ticket = null;
		long now = WxSignUtil.createTimestamp();
		if (accessToken == null || (now - timestamp) > 7200L
				|| isInvalid(accessToken)) {// 过期以access token 的时间为准
			WxAccessToken wxAccessToken = updateAccessToken(mid);
			ticket = updateApiTicket(wxAccessToken.getAccessToken(),
					wxAccessToken.getTimestamp(), mid);
		} else if (sysInTicket == null || sysInTicket.getWxTicket() == null
				|| (now - sysInTicket.getExpiryTime()) > 7200L) {
			// accessToken有效，ticket不一定有效，仍要判断时间，因为accesstoken和ticket不是一一对应关系，
			// 此情况下更新ticket
			ticket = updateApiTicket(accessToken, timestamp, mid);
		} else {
			ticket = sysInTicket.getWxTicket();
		}

		return ticket;
	}

	/**
	 * 调用一个借口来验证accesstoken是否有效
	 * 
	 * @param accessToken
	 * @return
	 * @throws Exception
	 */
	private boolean isInvalid(String accessToken) {
		String jsapiTicketUrl = SysConstants.GET_IP_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = null;
		try {
			result = wposHttpClient.get(jsapiTicketUrl);
		} catch (Exception e) {
			e.printStackTrace();
			return true;
		}
		String errcode = result.getString("errcode");
		if (errcode != null && ("40001".equals(errcode) || "40014".equals(errcode) || "42001".equals(errcode))) {
			return true;
		}
		return false;
	}
	
	/**
	 * 请求微信 access token，并更新
	 * 
	 * @param mid
	 * @return
	 * @throws Exception
	 * @throws MangoException
	 */
	private WxAccessToken updateAccessToken(String mid) throws Exception {

		System.out.println("----------WxAuthentication.updateAccessToken------------");
		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		String appId = wxAuthorizationInfo.getWxAppId();
		String appsecret = wxAuthorizationInfo.getWxAppSecret();
		
		String accessTokenUrl = SysConstants.JS_API_ACCESS_TOKEN_URL.replace(
				SysConstants.APPID, appId).replace(SysConstants.APPSECRET,
				appsecret);

		JSONObject result = wposHttpClient.get(accessTokenUrl);
		String accessToken = result.getString("access_token");
		
		if (accessToken == null) {
			throw new Exception("请求微信accessToken失败");
		}
		
		Long timestamp = WxSignUtil.createTimestamp();
		
		wxAuthorizationInfo.setMerchantAccessToken(accessToken);
		wxAuthorizationInfo.setMerchantAccessTokenExpiry(timestamp);
		sysWxChildMerchantTokenService.updateMrtToken(wxAuthorizationInfo);
		
		WxAccessToken wxAccessToken = new WxAccessToken();
		wxAccessToken.setAccessToken(accessToken);
		wxAccessToken.setTimestamp(timestamp);
		wxAccessToken.setMid(mid);

		return wxAccessToken;
	}

	/**
	 * 请求微信js api ticket，并更新，有效时间以access token的过期时间为准
	 * 
	 * @param accessToken
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	private String updateJsTicket(String accessToken, Long timestamp, String mid)
			throws Exception {
		System.out.println("----------WxAuthentication.updateJsTicket------------");
		String jsapiTicketUrl = SysConstants.JS_API_TICKET_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = wposHttpClient.get(jsapiTicketUrl);

		String jsapi_ticket = result.getString("ticket");
		// 处理特殊情况。重新更新access token,再请求ticket一次
		if (jsapi_ticket == null) {
			WxAccessToken wxAccessToken = updateAccessToken(mid);
			accessToken = wxAccessToken.getAccessToken();
			timestamp = wxAccessToken.getTimestamp();
			jsapiTicketUrl = SysConstants.JS_API_TICKET_URL.replace(
					SysConstants.ACCESS_TOKEN, accessToken);
			result = wposHttpClient.get(jsapiTicketUrl);
			jsapi_ticket = result.getString("ticket");
		}
		if (jsapi_ticket == null) {
			throw new Exception("请求微信js ticket失败");
		}
		
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.JSNNAME, mid);
		
		// 删除原有记录,WxTicket为主键不能做update操作
		if (sysInTicket != null) {
			sysInTicketService.del(sysInTicket);
		}
		// 产生新纪录
		sysInTicket = new SysInTicket();
		sysInTicket.setApiSymbol(SysConstants.JSNNAME);
		sysInTicket.setMid(mid);
		sysInTicket.setWxTicket(jsapi_ticket);
		sysInTicket.setExpiryTime(timestamp);
		sysInTicketService.saveSysInTicket(sysInTicket);
		return jsapi_ticket;
	}

	/**
	 * 请求微信js api ticket，并更新，有效时间以access token的过期时间为准
	 * 
	 * @param accessToken
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	private String updateApiTicket(String accessToken, Long timestamp,
			String mid) throws Exception {
		System.out.println("----------WxAuthentication.updateApiTicket------------");
		String jsapiTicketUrl = SysConstants.CARD_API_TICKET_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = wposHttpClient.get(jsapiTicketUrl);

		String jsapi_ticket = result.getString("ticket");
		// 处理特殊情况。重新更新access token,再请求ticket一次
		if (jsapi_ticket == null) {
			WxAccessToken wxAccessToken = updateAccessToken(mid);
			accessToken = wxAccessToken.getAccessToken();
			timestamp = wxAccessToken.getTimestamp();
			jsapiTicketUrl = SysConstants.CARD_API_TICKET_URL.replace(
					SysConstants.ACCESS_TOKEN, accessToken);
			result = wposHttpClient.get(jsapiTicketUrl);
			jsapi_ticket = result.getString("ticket");
		}
		
		// 再次请求后还未获取到js ticket 就报错
		if (jsapi_ticket == null) {
			throw new Exception("请求微信js ticket失败");
		}
		
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.CARDNAME, mid);

		// 删除原有记录,WxTicket为主键不能做update操作
		if (sysInTicket != null) {
			sysInTicketService.del(sysInTicket);
		}
		// 产生新纪录
		sysInTicket = new SysInTicket();
		sysInTicket.setApiSymbol(SysConstants.CARDNAME);
		sysInTicket.setMid(mid);
		sysInTicket.setWxTicket(jsapi_ticket);
		sysInTicket.setExpiryTime(timestamp);
		sysInTicketService.saveSysInTicket(sysInTicket);

		return jsapi_ticket;
	}

}
